mod rect;
pub mod measure;

pub use rect::Rect;

use std::ops::{Add, AddAssign};

use cgmath::{vec2, Deg, Matrix4, SquareMatrix, Vector2};

#[derive(Clone, Copy)]
pub enum Rotation {
    Idle,
    QuaterCw,
    Half,
    QuaterCcw,
}

impl Rotation {
    pub fn rotate_ccw(&mut self) {
        *self = match self {
            Rotation::Idle => Self::QuaterCcw,
            Rotation::QuaterCw => Self::Idle,
            Rotation::QuaterCcw => Self::Half,
            Rotation::Half => Self::QuaterCw,
        }
    }

    pub fn rotate_cw(&mut self) {
        *self = match self {
            Rotation::Idle => Self::QuaterCw,
            Rotation::QuaterCw => Self::Half,
            Rotation::QuaterCcw => Self::Idle,
            Rotation::Half => Self::QuaterCcw,
        }
    }

    pub fn into_matrix(self) -> Matrix4<f32> {
        match self {
            Rotation::Idle => Matrix4::identity(),
            Rotation::QuaterCw => Matrix4::from_angle_z(Deg(90.0)),
            Rotation::Half => Matrix4::from_angle_z(Deg(180.0)),
            Rotation::QuaterCcw => Matrix4::from_angle_z(Deg(-90.0)),
        }
    }
}

impl Add for Rotation {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        unsafe {
            let (lhs, rhs) = std::mem::transmute::<_, (u8, u8)>((self, rhs));
            std::mem::transmute((lhs + rhs) % 4)
        }
    }
}

impl AddAssign for Rotation {
    fn add_assign(&mut self, rhs: Self) {
        *self = *self + rhs
    }
}

pub trait PointExt {
    fn rotate(&self, pivot: Self, rot: Rotation) -> Self;
}

impl PointExt for Vector2<i32> {
    fn rotate(&self, pivot: Vector2<i32>, rot: Rotation) -> Self {
        let dir = *self - pivot;
        match rot {
            Rotation::QuaterCw => pivot + vec2(-dir.y, dir.x),
            Rotation::QuaterCcw => pivot + vec2(dir.y, -dir.x),
            Rotation::Half => pivot - dir,
            Rotation::Idle => *self,
        }
    }
}
